#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##-- G15 -- S09 ------------------------------------------------------------##
__all__ = []

from .. import autostacks
from .. import formlib as layout
from .. import pilelib as stack
from .. import settings as c
from .. import stackfill as fill
from ..gamelib import info as gi, state, game
from ..hint import DefaultHint
from ..packlib import info as cs
from ..pilelib import move as a_move
from ..piles import pairing
from . import pyramid

GT, SL = gi.GT, gi.SL
StackWrapper = stack.StackWrapper


class MonteCarlo_Talon(stack.Talon):
    """
    Move cards by Game.FillEmptyStacks() when _row_ has gap or is free.
    """

    def can_deal_cards(self):
        """
        can_deal_cards() -> bool

        Check by Game.talon_deal() if pile can deal more cards. Default is
        while there are cards and rowstacks without cards available.

        :return: deal more cards?
        :rtype: boolean
        """
        free = False
        for row in self.game.s['rows']:
            if not row.cards:
                free = True
            elif free and self.game.isfreeRow(row.id):
                return True
        return free and self.cards

    def deal_cards(self, sound=False):
        """
        deal_cards([sound:bool=False]) -> None

        Deal cards for Game.talon_deal() to other piles. Default is to call
        Game.fillEmptyStacks().

        :keyword boolean sound: sound fx?
        """
        game = self.game
        game._move_do(a_move.AUpdateStackMove(game.s['talon'], c.UNDO))  # low level
        ncards = game.fillEmptyStacks()
        game._move_do(a_move.AUpdateStackMove(game.s['talon'], c.REDO))  # low level
        return ncards


class DerLetzteMonarch_Foundation(stack.SS_Foundation):
    """
    Build up by rank in same suit from _reserve_.
    """

    def cardsBuild(self, from_stack, cards):
        """
        cardsBuild(from_stack:obj, cards:tup) -> bool

        Check if pile can build with cards from _reserve_.

        :param object from_stack: pile playing cards.
        :param tuple cards: cards playing on pile.
        :return: pile can build with cards from played from_stack?
        :rtype: boolean

        seealso:
          Method pysollib.stackcaps.builds for the *capability* check.

        note::
            Cards from other groups handled by
            DerLetzteMonarch_RowStack._dropPairMove() to pile.
        """
        cards = cards or from_stack.cards
        if not super(DerLetzteMonarch_Foundation).cardsBuild(from_stack,
                                                             cards):
            return False
        return not cards or from_stack in self.game.s['reserves']


class DerLetzteMonarch_RowStack(stack.ReserveStack):
    """
    Drop to_stack on _foundation_ when build and move from_stack to its place
    else move from_stack on _reserve_.
    """

    def cardsDrop(self, stacks):
        """
        cardsDrop(stacks:tup) -> tup

        Overide to not allow dropping.

        :param tuple stacks: usually game.s.foundations
        :return: pile receiving dropped cards and number of cards.
        :rtype: tuple
        """
        return (None, 0)

    def cardsBuild(self, from_stack, cards):
        """
        cardsBuild(from_stack:obj, cards:tup) -> bool

        Check if pile can build with cards from from_stack. Addition must be
        Game.isNeighbour() and _getDropStack() must find a playable foundation
        or reserve.

        :param object from_stack: pile playing cards.
        :param tuple cards: cards playing on pile.
        :return: pile can build with cards from played from_stack?
        :rtype: boolean

        seealso:
          Method pysollib.stackcaps.builds for the *capability* check.
        """
        if not super(DerLetzteMonarch_RowStack, self).cardsBuild(from_stack,
                                                                 cards):
            return False
        if not self.game.isNeighbour(from_stack, self):
            return False
        return self._DROPSTACK is not None

    @property
    def _DROPSTACK(self):
        """
        _DROPSTACK -> tup

        Overide to not allow dropping.

        :param tuple stacks: usually game.s.foundations
        :return: pile receiving dropped cards and number of cards.
        :rtype: tuple
        """
        if len(self.cards) != 1:
            return
        s = self.game.s
        for stack in s['foundations']:
            if stack.cardsBuild(self, self.cards):
                return stack
        for stack in s['reserves']:
            if not stack.cards:
                return stack

    def move_pile(self, ncards, to_stack, **kw):
        """
        Overide to move to call drop neighbor method before fill stack method.

        .. move_pile(ncards:int, to_stack:Pile[, *animation keywords*]) -> None
        """
        assert ncards == 1 and to_stack in self.game.s['rows']
        assert len(to_stack.cards) == 1
        self._drop_neighbor(ncards, to_stack, **kw)
        self.fill_stack()

    @state.move_state('fill')
    def _drop_neighbor(self, ncards, to_stack, **kw):
        """
        Drop top ncards of stack on s[foundations] and move self to stack.

        .. \_drop_neighbor(ncards:int, to_stack:Pile[, *animation keywords*])
             -> None
        """
        assert to_stack is not None
        dropstack = to_stack._DROPSTACK
        self.game._move_do(a_move.AMoveMove(ncards, to_stack, dropstack, **kw),
                            reset=True)  # low level
        self.game._move_do(a_move.AMoveMove(ncards, self, to_stack, **kw),
                            reset=True)  # low level


class DropReserve(stack.SingleAsDoubleClickMethod, stack.ReserveStack):
    """
    Build any 1 card. Drop card on _foundation_.
    """
    pass


class SimpleCarlo(game.Game):
    """
    Drop cards on _foundation_ within a turn.

    _row_
      Pile 25 over 5 row. Init deal 1 card. Pair by same rank. Play top. Drop
      pair on _foundation_. Base none.

    _talon_
      Pile 1 SE. Fill gap with neighbor _row then deal 1 card on gap remain.

    _foundation_
      Pile 1 NE.

    note::
     ⟪autoflip⟫ ⟪autodrop⟫ ⟪quickplay⟫ disabled.
    ------
    :cvar Stack _talon_: MonteCarlo_Talon
    :cvar Stack _foundation_: stack.SingleFoundation
    :cvar Stack _row_: pairing.PairNeighborRK_RowStack
    :cvar Hint _hint_: hint.DefaultHint
    """
    __info__ = dict(id=90, name="Simple Carlo", game_type=(GT.PAIRING |
                    GT.CHILDREN), decks=1, version='3.00',
                    skill_level=SL.MOSTLY_LUCK)
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = MonteCarlo_Talon
    _foundation_ = stack.SingleFoundation
    _row_ = pairing.PairNeighborRK_RowStack
    _hint_ = DefaultHint

    def _create_game(self, rows=(5,) * 5, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + max(rows) * l.XS + 3 * l.HXS,
                            l.YM + len(rows) * l.YS)
        y = l.YM
        for row in rows:  # Vert
            x = l.XM
            for j in range(rows):  # Horz
                s['rows'].append(self._row_(x, y, game=self))
                x += l.XS
            y += l.YS
        x, y = self.width - l.XS, l.YM
        s['foundations'].append(self._foundation_(x, y, game=self))
        l.set_ncards_text(s['foundations'][0], "s")
        y += 2 * l.YS
        s['talon'] = self._talon_(x, y, game=self, )
        l.set_ncards_text(s['talon'], "s", text_format="%D")
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        self._play_sound('deal')
        self.s['talon'].dealRow()

    def _autopiles(self, event=None):
        """
        Return piles for auto or quick move.

        Overide to disable autoflip, autodrop, quickplay.

        .. \_autopiles(self, event=None) -> seq(seq(Pile ...), seq(Pile ...),
             seq(Pile ...))
        """
        return autostacks.none()

    def isNeighbour(self, stack1, stack2):
        """
        isNeighbour(stack1, stack2) -> bool

        Check by _row_ cardsBuild() if correct piles are neighbors. Any maybe.

        :return: row is free?
        :rtype: boolean
        """
        return 0 <= stack1.id <= 24 and 0 <= stack2.id <= 24

    def isFreeRow(self, id_):
        """
        isfreeRow() -> bool

        Check by can_deal_cards() if correct piles are free. Default is always.

        :return: row is free?
        :rtype: boolean
        """
        return True

    def fillEmptyStacks(self):
        free, ncards = 0, 0
        if self.can_play_sound:
            self._play_sound('deal')
        for row in self.s['rows']:
            assert len(row.cards) <= 1
            if not row.cards:
                free += 1
            elif free > 0 and self.isFreeRow(row.id):
                row.move_pile(1, self.allstacks[row.id - free], frames=4,
                              shadow=0)
        if free > 0:
            for row in (r for r in self.s['rows'] if not r.cards):
                if not self.s['talon'].cards:
                    break
                self.fill(self.s['talon'], row)
                ncards += 1
        self._stop_sound()
        return ncards + free


class MonteCarlo(SimpleCarlo):
    """
    Drop cards on _foundation_ within a turn.

    _row_
      Pile 25 over 5 row. Init deal 1 card. Pair with neighbor by same rank.
      Play top. Drop pair on _foundation_. Base none.

    _talon_
      Pile 1 SE. Fill gap with neighbor _row then deal 1 card on gap remain.

    _foundation_
      Pile 1 NE.

    note::
     ⟪autoflip⟫ ⟪autodrop⟫ disabled.
    ------
    :cvar Stack _talon_: MonteCarlo_Talon
    :cvar Stack _foundation_: stack.SingleFoundation
    :cvar Stack _row_: pairing.PairNeighborRK_RowStack
    :cvar Hint _hint_: Default_Hint
    """
    __info__ = dict(id=89, name="Monte Carlo", game_type=(GT.PAIRING |
                    GT.GNOME | GT.XPAT), decks=1, skill_level=SL.MOSTLY_LUCK,
                    altnames=("Quilt",), version='3.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    def _autopiles(self, event=None):
        """
        Return piles for auto or quick move.

        Overide to disable autoflip, autodrop.

        .. \_autopiles(self, event=None) -> seq(seq(Pile ...), seq(Pile ...),
             seq(Pile ...))
        """
        return autostacks.quickplay(super(MonteCarlo, self)._autopiles(event))

    def isNeighbour(self, stack1, stack2):
        """
        isNeighbour(stack1, stack2) -> bool

        Check by _row_ cardsBuild() if correct piles are neighbors. Only next.

        :return: row is free?
        :rtype: boolean
        """
        if not (0 <= stack1.id <= 24 and 0 <= stack2.id <= 24):
            return False
        return stack1.id - stack2.id in {0: (-5, -4, 1, 5, 6),
                                         4: (-6, -5, -1, 4, 5),
                                        }.get(stack2.id % 5,
                                            (-6, -5, -4, -1, 1, 4, 5, 6))


class MonteCarlo2Decks(MonteCarlo):
    """
    Drop cards on _foundation_ within a turn.

    _row_
      Pile 25 over 5 row. Init deal 1 card. Pair with neighbor by same rank.
      Play top. Drop pair on _foundation_. Base none.

    _talon_
      Pile 1 SE. Fill gap with neighbor _row then deal 1 card on gap remain.

    _foundation_
      Pile 1 NE.

    note::
     ⟪autoflip⟫ ⟪autodrop⟫ disabled.
    ------
    :cvar Stack _talon_: MonteCarlo_Talon
    :cvar Stack _foundation_: stack.SingleFoundation
    :cvar Stack _row_: pairing.PairNeighborRK_RowStack
    :cvar Hint _hint_: Default_Hint
    """
    __info__ = dict(id=216, name="Monte Carlo (2 decks)",
                    game_type=GT.PAIRING, decks=2,
                    skill_level=SL.MOSTLY_LUCK, version='4.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70


class Weddings(MonteCarlo):
    """
    Drop cards on _foundation_ within a turn.

    _row_
      Pile 25 over 5 row. Init deal 1 card. Pair by same rank when N has gap.
      Play top. Drop pair on _foundation_. Base none.

    _talon_
      Pile 1 SE. Fill gap with neighbor _row then deal 1 card on gap remain.

    _foundation_
      Pile 1 NE.

    note::
     ⟪autoflip⟫ ⟪autodrop⟫ disabled.
    ------
    :cvar Stack _talon_: MonteCarlo_Talon
    :cvar Stack _foundation_: stack.SingleFoundation
    :cvar Stack _row_: pairing.PairNeighborRK_RowStack
    :cvar Hint _hint_: Default_Hint
    """
    __info__ = dict(id=212, name="Weddings", game_type=GT.PAIRING,
                    decks=1, skill_level=SL.MOSTLY_LUCK,
                    version='4.60')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    def isFreeRow(self, id_):
        """
        isfreeRow() -> bool

        Check by can_deal_cards() if pile N are free.

        :return: row is free?
        :rtype: boolean
        """
        k = id_
        while k >= 5 and not self.game.allstacks[k - 5].cards:
            k -= 5
        return k != id_


class SimplePairs(MonteCarlo):
    """
    Drop cards on _foundation_ within a turn.

    _row_
      Pile 9 over 3 row. Init deal 1 card. Pair by same rank. Play top. Drop
      pair on _foundation_. Gap deal 1 card from _talon_. Base none.

    _talon_
      Pile 1 W.

    _foundation_
      Pile 1 E.

    note::
     ⟪autoflip⟫ ⟪autodrop⟫ disabled.
    ------
    :cvar Stack _talon_: stack.Talon
    :cvar Stack _foundation_: stack.SingleFoundation
    :cvar Stack _row_: pairing.PairNeighborRK_RowStack
    :cvar Hint _hint_: Default_Hint
    """
    __info__ = dict(id=91, name="Simple Pairs", game_type=(GT.PAIRING |
                    GT.CHILDREN | GT.GNOME), decks=1, skill_level=SL.LUCK,
                    altnames=("Jamestown",), version='3.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = stack.Talon

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 6 * l.XS, l.YM + 4 * l.YS)
        y = l.YM + l.YS / 2
        for row in (3,) * 3:  # Vert
            x = l.XM + 3 * l.XS / 2
            for j in range(row):  # Horz
                s['rows'].append(self._row_(x, y, game=self))
                x = 2 * l.XS
            y += 2 * l.YS
        x, y = l.XM, l.YM + 3 * l.YS / 2
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], "s")
        x = self.width - l.XS
        s['foundations'].append(self._foundation_(x, y, game=self))
        l.set_ncards_text(s['foundations'][0], "s")
        l.defaultStackGroups()
        return l

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _talon_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromTalon(self, stack, self.s['rows'])

    def isNeighbour(self, stack1, stack2):
        """
        isNeighbour(stack1, stack2) -> bool

        Check by _row_ cardsBuild() if correct piles are neighbors. Any maybe.

        :return: row is free?
        :rtype: boolean
        """
        return 0 <= stack1.id <= 15 and 0 <= stack2.id <= 15


class Neighbour(SimpleCarlo):
    """
    Drop cards on _foundation_ within a turn.

    _row_
      Pile 25 over 5 row. Init deal 1 card. Pair with neighbor by sum 13. Play
      top. Drop pair or KING on _foundation_. Gap deal cards. Base none.

    _talon_
      Pile 1 SE. Fill gap with neighbor _row then deal 1 card on gap remain.

    _foundation_
      Pile 1 NE.

    note::
     ⟪autoflip⟫, ⟪quickplay⟫ disabled.
    ------
    :cvar Stack _talon_: MonteCarlo_Talon
    :cvar Stack _foundation_: pyramid.Pyramid_Foundation
    :cvar Stack _row_: pairing.SumNeighborKing_RowStack
    :cvar Hint _hint_: Default_Hint
    """
    __info__ = dict(id=92, name="Neighbour", game_type=(GT.PAIRING |
                    GT.GNOME), decks=1, skill_level=SL.MOSTLY_LUCK,
                    version='3.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _foundation_ = pyramid.Pyramid_Foundation
    _row_ = pairing.SumNeighborKing_RowStack

    def _autopiles(self, event=None):
        """
        Return piles for auto or quick move.

        Overide to disable autoflip, quickplay.

        .. \_autopiles(self, event=None) -> seq(seq(Pile ...), seq(Pile ...),
             seq(Pile ...))
        """
        return autostacks.drop(super(Neighbour, self)._autopiles(event))

    def isNeighbour(self, stack1, stack2):
        """
        isNeighbour(stack1, stack2) -> bool

        Check by _row_ cardsBuild() if correct piles are neighbors. Only next.

        :return: row is free?
        :rtype: boolean
        """
        if not (0 <= stack1.id <= 24 and 0 <= stack2.id <= 24):
            return False
        return stack1.id - stack2.id in {0: (-5, -4, 1, 5, 6),
                                         4: (-6, -5, -1, 4, 5),
                                        }.get(stack2.id % 5,
                                            (-6, -5, -4, -1, 1, 4, 5, 6))

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _talon_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromTalon(self, stack, self.s['rows'])


class Fourteen(game.Game):
    """
    Drop cards on _foundation_ within a turn.

    _row_
      Pile 12 over 2 row. Init deal [5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
      4, 4] open card. Pair by sum 14. Play top. Drop pair on _foundation_.
      Base none.

    _foundation_
      Pile 1 NE.

    note::
     ⟪autoflip⟫ ⟪autodrop⟫ disabled.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SingleFoundation
    :cvar Stack _row_: pairing.SumNeighborFourteen_RowStack
    """
    __info__ = dict(id=96, name="Fourteen", game_type=(GT.PAIRING |
                    GT.OPEN | GT.CHILDREN | GT.GNOME), decks=1,
                    skill_level=SL.MOSTLY_LUCK, version='3.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _foundation_ = stack.SingleFoundation
    _row_ = pairing.SumNeighborFourteen_RowStack

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 7 * l.XS, l.YM + 5 * l.YS)
        y = l.YM
        for row in (6,) * 2:  # Vert
            x = l.XM
            for j in range(row):  # Horz
                s['rows'].append(self._row_(x, y, game=self))
                x + l.XS
            y += 5 * l.YS / 2
        y = l.YM
        s['foundations'].append(self._foundation_(x, y, game=self))
        l.set_ncards_text(s['foundations'][0], "s")
        s['talon'] = self._talon_(self.width - l.XS, self.height - l.YS,
                               game=self)
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        for i in range(3):
            self.s['talon'].dealRow(frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].dealRow(rows=self.s['rows'][:4])

    def _autopiles(self, event=None):
        """
        Return piles for auto or quick move.

        Overide to disable autoflip, autodrop.

        .. \_autopiles(self, event=None) -> seq(seq(Pile ...), seq(Pile ...),
             seq(Pile ...))
        """
        return autostacks.quickplay(super(MonteCarlo, self)._autopiles(event))


class Nestor(game.Game):
    """
    Drop cards on _foundation_ within a turn.

    _row_
      Pile 12 over 2 (8/4). Init deal 6 open card so no pile has same rank.
      Pair by same rank. Play top. Drop pair on _foundation_. Base none.

    _foundation_
      Pile 1 SE.

    note::
     ⟪autoflip⟫ ⟪autodrop⟫ disabled.

    seealso::
      http://en.wikipedia.org/wiki/Nestor_%28solitaire%29
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SingleFoundation
    :cvar Stack _row_: pairing.PairRK_RowStack
    """
    __info__ = dict(id=235, name="Nestor", game_type=(GT.PAIRING |
                    GT.OPEN), decks=1, version='3.00',
                    skill_level=SL.MOSTLY_LUCK)
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _foundation_ = stack.SingleFoundation
    _row_ = pairing.PairRK_RowStack

    def _create_game(self, rows=(8, 4,), playcards=12, **kw):
        l, s = layout.Layout(self), self.s
        max_rows = max(rows)
        self.table_size = (l.XM + (max_rows + 1) * l.XS,
                     l.YM + 2 * l.YS + playcards * l.YOFFSET)
        x, y = l.XM, l.YM
        for row in rows:  # Vert
            x = l.XM + (max_rows - row) * l.XS
            for i in range(row):  # Horz
                s['rows'].append(self._row_(x, y, game=self))
                x += l.XS
            y = self.height - l.YS
        x, y = self.width - l.XS
        s['foundations'].append(self._foundation_(x, y, game=self))
        l.set_ncards_text(s['foundations'][0], "n")
        x = l.XM
        s['talon'] = self._talon_(x, y, game=self)
        l.defaultStackGroups()
        return l

    def _checkRow(self, cards):
        for i, card1 in enumerate(cards):
            for j, card2 in zip(range(i), cards[i:]):
                if card1['rank'] == card2['rank']:
                    return j

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return no more than 2 of same rank in pile for _row_ deal.
        """
        # no row will have two cards of the same rank
        ncards = self['ncards']
        for i, j in zip(range(8), range(1, 9)):
            for t in range(1000):  # just in case
                k = self._checkRow(cards[i * 6:j * 6])
                if k is None:
                    break
                k += i * 6
                l = self.random.choice(range(j * 6, ncards))
                cards[k], cards[l] = cards[l], cards[k]
        cards.reverse()
        return cards

    def _start(self, **kw):
        for r in self.s['rows'][:8]:
            for j in range(6):
                self.s['talon'].dealRow(rows=[r], frames=0)
        self._play_sound('deal')
        self.s.talon['dealRow'](rows=self.s['rows'][8:])

    def _autopiles(self, event=None):
        """
        Return piles for auto or quick move.

        Overide to disable autoflip, autodrop.

        .. \_autopiles(self, event=None) -> seq(seq(Pile ...), seq(Pile ...),
             seq(Pile ...))
        """
        return autostacks.quickplay(super(MonteCarlo, self)._autopiles(event))


class Vertical(Nestor):
    """
    Drop cards on _foundation_ within a turn.

    _row_
      Pile 17 over 2 (7/9). Init deal [6, 6, 6, 7, 6, 6, 6] open card so no
      pile has same rank. Pair by same rank. Play top. Drop pair on
      _foundation_. Base none.

    _foundation_
      Pile 1 SE.

    note::
     ⟪autoflip⟫ ⟪autodrop⟫ disabled.

    seealso::
      http://en.wikipedia.org/wiki/Nestor_%28solitaire%29
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SingleFoundation
    :cvar Stack _row_: pairing.PairRK_RowStack
    """
    __info__ = dict(id=368, name="Vertical", game_type=(GT.PAIRING |
                    GT.OPEN), decks=1, version='0.90fc',
                    skill_level=SL.MOSTLY_LUCK)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, **kw):
        kw.setdefault('rows', (7, 9))
        return super(Vertical, self)._create_game(**kw)

    def _start(self, **kw):
        self.s['talon'].dealRow(frames=0)
        for i in range(4):
            self.s['talon'].dealRow(rows=self.s['rows'][:7], frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['rows'][:7])
        self.s['talon'].dealRow(rows=[self.s['rows'][3]])


class TheWishOpen(MonteCarlo):
    """
    Drop cards on _foundation_ within a turn.

    _row_
      Pile 8 over 2 row. Init deal 4 open card. Pair by same rank. Play top.
      Drop pair on _foundation_. Base none.

    _foundation_
      Pile 1 SE.

    note::
      ⟪autoflip⟫ ⟪autodrop⟫ ⟪quickplay⟫ disabled.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SingleFoundation
    :cvar Stack _row_: pairing.PairRK_RowStack
    :cvar Hint _hint_: Default_Hint
    """
    __info__ = dict(id=329, name="The Wish (Open)", game_type=(GT.PAIRING
                    | GT.OPEN | GT.ORIGINAL), decks=1,
                    skill_level=SL.MOSTLY_SKILL, ranks=cs.SHORT_A7K,
                    version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _row_ = pairing.PairRK_RowStack

    def _create_game(self, rows=8, **kw):
        l, s = layout.Layout(self), self.s
        dy = l.YS + 3 * l.YOFFSET
        self.table_size = (l.XM + 6 * l.XS, 2 * l.YM + 2 * dy)
        y = l.YM
        for row in range(rows / 4):  # Vert
            x = l.XM
            for j in range(4):  # Horz
                s['rows'].append(self._row_(x, y, game=self))
                x += l.XS
            y += l.YM + dy
        x, y = self.width - l.XS, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        x, y = self.width - l.XS, self.height - l.YS
        s['foundations'].append(self._foundation_(x, y, game=self))
        l.set_ncards_text(s['foundations'][0], "n")
        l.defaultStackGroups()
        return l

    def _start(self, flip=True, **kw):
        for i in range(3):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()


class TheWish(TheWishOpen):
    """
    Drop cards on _foundation_ within a turn.

    _row_
      Pile 8 over 2 row. Init deal 4 card. Pair with neighbor by same rank.
      Play top. Drop pair on _foundation_. Base none.

    _foundation_
      Pile 1 SE.

    note::
      ⟪autoflip⟫ ⟪autodrop⟫ ⟪quickplay⟫ disabled.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SingleFoundation
    :cvar Stack _row_: pairing.PairRK_RowStack
    :cvar Hint _hint_: Default_Hint
    """
    __info__ = dict(id=328, name="The Wish", game_type=GT.PAIRING,
                    decks=1, skill_level=SL.MOSTLY_LUCK,
                    ranks=cs.SHORT_A7K, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _row_ = pairing.PairRK_RowStack

    def _start(self, flip=False, **kw):
        kw.setdefault('flip', False)
        super(TheWish, self)._start(**kw)

    def _auto_fill(self, stack):
        """
        flip stack if cards.

        *Parameters*:
          stack : Stack
            pile just played
        """
        if stack.cards:
            self.flip(stack)


class DerLetzteMonarch(game.Game):
    """
    Drop cards on _foundation_ except KING within a turn.

    _row_
      Pile 52 over 4 row. Init deal 1 card. Drop neighbor W N E S on
      _foundation_ when build else _reserve_.

    _reserve_
      Pile 4 horz N _row_. Drop 1 card from _row_. Play top on _foundation_.

    _foundation_
      Pile 1 NE. Build up in rank by same suit. Play none.

    note::
      ⟪autodrop⟫ limited to _reserve_.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: DerLetzteMonarch_Foundation (max_move=0)
    :cvar Stack _row_: DerLetzteMonarch_RowStack (max_accept=1, max_card=2)
    :cvar Stack _reserve_: DropReserve (max_accept=1,
      max_card=2)
    """
    __info__ = dict(id=152, name="The Last Monarch", game_type=(GT.ONE_DECK
                    | GT.OPEN), decks=1, version='3.40',
                    skill_level=SL.MOSTLY_SKILL,
                    altnames=("Der letzte Monarch",))
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _foundation_ = StackWrapper(DerLetzteMonarch_Foundation, max_move=0)
    _row_ = StackWrapper(DerLetzteMonarch_RowStack, max_accept=1,
                          max_cards=2)
    _reserves_ = StackWrapper(DropReserve, max_accept=0)

    def _create_game(self, rows=(13,) * 4, reserves=4, texts=False, **kw):
        l, s = layout.Layout(self, card_x_space=4), self.s
        decks = l.NDECKS
        dx = (0 if decks == 1 else l.XS)
        self.table_size = (l.XM + (13 if decks == 1 else 15) * l.XS,
                     l.YM + 5 * l.YS)
        y = l.YM + l.YS
        for row in rows:
            x = dx + l.XM
            for j in range(row):
                s['rows'].append(self._row_(x, y, game=self))
                x += l.XS
            y += l.YS
        x, y = l.XM + 2 * l.XS, l.YM
        for i in range(4):
            s['reserves'].append(self._reserves_(x, y, game=self))
            x += l.XS
        x += l.XS
        for i in l.SUITS:
            s['foundations'].append(self._foundation_(x, y, game=self, suit=i))
            x += l.XS
        s['talon'] = self._talon_(l.XM, l.YM, self)
        if texts:
            l.set_ncards_text(s['talon'], 'ne')
        self.sg['stack.Talons'] = [s['talon']]
        self.sg['openstacks'] = s['foundations'] + s['rows']
        self.sg['dropstacks'] = s['rows'] + s['reserves']
        self.sg['reservestacks'] = s['reserves']
        return l

    def _start(self, **kw):
        self.s['talon'].dealRow(rows=self.s['rows'][:39], frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['rows'][39:])

    @property
    def is_complete(self):
        cards = sum([len(s.cards) for s in self.s['foundations']])
        return cards == self['ncards'] - 1

    def _autopiles(self, event=None):
        """
        Return piles for auto or quick move.

        Overide to disable autoflip, quickplay. Also use s[reserve] only.

        .. \_autopiles(self, event=None) -> seq(seq(Pile ...), seq(Pile ...),
             seq(Pile ...))
        """
        return ((), self.s['reserves'], ())

    def isNeighbour(self, stack1, stack2):
        """
        isNeighbour(stack1, stack2) -> bool

        Check by _row_ cardsBuild() if correct piles are neighbors. Only next.

        :return: row is free?
        :rtype: boolean
        """
        if not (0 <= stack1.id <= 51 and 0 <= stack2.id <= 51):
            return False
        return stack1.id - stack2.id in {0: (-13, 1, 13),
                                         12: (-13, 1, 13),
                                        }.get(stack2.id % 13,
                                            (-13, -1, 1, 13))


class TheLastMonarchII(DerLetzteMonarch):
    """
    Drop cards on _foundation_ within a turn.

    _row_
      Pile 52 over 4 row. Init deal 1 card. Drop neighbor W N E S on
      _foundation_ when build else _reserve_. Gap deal 1 card from _talon_.

    _reserve_
      Pile 4 horz N _row_. Drop 1 card from _row_. Play top on _foundation_.

    _foundation_
      Pile 1 NE. Build up in rank by same suit. Play none.

    note::
      ⟪autodrop⟫ limited to _reserve_.
    ------
    :cvar Stack _talon_: stack.Talon
    :cvar Stack _foundation_: DerLetzteMonarch_Foundation (max_move=0)
    :cvar Stack _row_: DerLetzteMonarch_RowStack (max_accept=1, max_card=2)
    :cvar Stack _reserve_: DropReserve (max_accept=1,
      max_card=2)
    """
    __info__ = dict(id=663, name="The Last Monarch Ⅱ",
                    game_type=(GT.TWO_DECK | GT.ORIGINAL), decks=2,
                    skill_level=SL.MOSTLY_SKILL,
                    version='0.94fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.Talon

    def _create_game(self, **kw):
        kw.setdefault('texts'. True)
        return super(TheLastMonarchII, self)._create_game(**kw)

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _talon_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromTalon(self, stack, self.s['rows'])


class DoubletsII(game.Game):
    """
    Drop cards on _foundation_ within a turn.

    _row_
      Pile 12 horz. Init deal 4 card. Drop pair by same rank on _foundation_.
      Gap deal 1 card from _talon_.

    _talon_
      Pile 1 SW.

    _foundation_
      Pile 1 SE.
    ------
    :cvar Stack _talon_: stack.Talon
    :cvar Stack _foundation_: stack.SingleFoundation
    :cvar Stack _row_: pairing.PairRK_RowStack
    """
    __info__ = dict(id=649, name="Doublets Ⅱ", game_type=GT.PAIRING,
                    decks=1, skill_level=SL.MOSTLY_LUCK,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.Talon
    _foundation_ = stack.SingleFoundation
    _row_ = pairing.PairRK_RowStack

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 12 * l.XS, l.YM + 2 * l.YS + l._get_stack_height(3, l.YS),)

        x, y = l.XM, l.YM
        for i in range(12):
            s.rows['append'](self._row_(x, y, game=self))
            x += l.XS
        x, y = l.XM, self.height - l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'n')
        x, y = self.width - l.XS, self.height - l.YS
        s['foundations'].append(self._foundation_(x, y, game=self))
        l.set_ncards_text(s['foundations'][0], "n")
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        for i in range(3):
            self.s['talon'].dealRow(flip=True, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _talon_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromTalon(self, stack, self.s['rows'])


class RightAndLeft(game.Game):
    """
    Drop cards on _foundation_ within a turn.

    _row_
      Pile 2 horz. Init deal 1 card. Drop pair by same rank on _foundation_.

    _talon_
      Pile 1 S. Deal 1 card on _row_

    _foundation_
      Pile 1 E.
    ------
    :cvar Stack _talon_: stack.DealRowRedealTalon (max_rounds=INFINITE)
    :cvar Stack _foundation_: stack.SingleFoundation
    :cvar Stack _row_: pairing.PairRK_RowStack
    """
    __info__ = dict(id=727, name="Right and Left", game_type=GT.PAIRING,
                    decks=2, redeals=cs.INFINITE, skill_level=SL.LUCK,
                    version='1.10fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = StackWrapper(stack.DealRowRedealTalon, max_rounds=cs.INFINITE)
    _foundation_ = stack.SingleFoundation
    _row_ = pairing.PairRK_RowStack

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 5 * l.XS, l.YM + 3 * l.YS)
        x, y = l.XM + l.XS, l.YM + 2 * l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'se')
        x, y = l.XM + l.XS / 2, l.YM
        for i in range(2):
            s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
            l.set_ncards_text(s['rows'][i], 's')
            x += l.XS
        x += 3 * l.XS / 2
        s['foundations'].append(self._foundation_(x, y, game=self))
        l.set_ncards_text(s['foundations'][0], 's')
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        self._play_sound('deal')
        self.s['talon'].dealRow()

PROVIDES = [MonteCarlo, MonteCarlo2Decks, Weddings, SimpleCarlo, SimplePairs,
           Neighbour, Fourteen, Nestor, DerLetzteMonarch, TheWish,
           TheWishOpen, Vertical, DoubletsII, TheLastMonarchII, RightAndLeft]